package moe.codeest.enviews;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.*;
import ohos.app.Context;

/**
 * Created by codeest on 2016/11/9.
 * <p>
 * 感觉是这个系列里设计的最赞的一款了 >//<
 */

public class ENDownloadView extends Component implements Component.DrawTask, Component.LayoutRefreshedListener {

    private static final int STATE_NONE = 4;

    private static final int STATE_PRE = 0;

    private static final int STATE_DOWNLOADING = 1;

    private static final int STATE_END = 2;

    private static final int STATE_RESET = 3;

    private static final int DEFAULT_LINE_COLOR = 0xffffffff;

    private static final int DEFAULT_BG_LINE_COLOR = 0xff3a3f45;

    private static final int DEFAULT_TEXT_COLOR = 0xffffffff;

    private static final int DEFAULT_LINE_WIDTH = 9;

    private static final int DEFAULT_BG_LINE_WIDTH = 9;

    private static final int DEFAULT_TEXT_SIZE = 20;

    private static final int DEFAULT_STATE = STATE_NONE;

    private static final int DEFAULT_RIPPLE_SPEED = 2;

    private static final int DEFAULT_DOWNLOAD_TIME = 2000;

    private static final DownloadUnit DEFAULT_DOWNLOAD_UNIT = DownloadUnit.B;

    private OnDownloadStateListener onDownloadStateListener;

    private int mCurrentState;

    private float mCurrentRippleX;

    private double mCurrentSize, mTotalSize;

    private int mTextSize;

    private int mDownloadTime;

    private DownloadUnit mUnit;

    private Paint mPaint, mBgPaint, mTextPaint;

    private Path mPath;

    private RectFloat mRectF, mClipRectF;

    private float mFraction;

    private float mWidth, mHeight;

    private float mCenterX, mCenterY;

    private float mBaseLength, mCircleRadius, mBaseRippleLength;

    private boolean isInit = false;

    public static enum DownloadUnit {
        GB,
        MB,
        KB,
        B,
        NONE;

        private DownloadUnit() {
        }
    }

    public ENDownloadView(Context context) {
        this(context, null);
    }


    public ENDownloadView(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public ENDownloadView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);

        int lineColor = AttrUtils.getColorFromAttr(attrSet, "download_line_color", DEFAULT_LINE_COLOR);
        int bgLineColor = AttrUtils.getColorFromAttr(attrSet, "download_bg_line_color", DEFAULT_BG_LINE_COLOR);
        int textColor = AttrUtils.getColorFromAttr(attrSet, "download_text_color", DEFAULT_TEXT_COLOR);
        int lineWidth = AttrUtils.getDimensionFromAttr(attrSet, "download_line_width", DEFAULT_LINE_WIDTH);
        int bgLineWidth = AttrUtils.getDimensionFromAttr(attrSet, "download_bg_line_width", DEFAULT_BG_LINE_WIDTH);
        int textSize = AttrUtils.getDimensionFromAttr(attrSet, "download_text_size", DEFAULT_TEXT_SIZE);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE_STYLE);
        mPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        mPaint.setStrokeWidth(lineWidth);
        mPaint.setColor(new Color(lineColor));

        mBgPaint = new Paint();
        mBgPaint.setAntiAlias(true);
        mBgPaint.setStyle(Paint.Style.STROKE_STYLE);
        mBgPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        mBgPaint.setStrokeWidth(bgLineWidth);
        mBgPaint.setColor(new Color(bgLineColor));

        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(new Color(textColor));
        mTextPaint.setTextSize(textSize);
        mTextPaint.setTextAlign(TextAlignment.CENTER);

        mPath = new Path();

        mTextSize = textSize;
        mCurrentState = DEFAULT_STATE;
        mUnit = DEFAULT_DOWNLOAD_UNIT;
        mDownloadTime = DEFAULT_DOWNLOAD_TIME;

        addDrawTask(this);
        setLayoutRefreshedListener(this);
    }

    public void setSize(int width, int height) {
        mWidth = width;
        mHeight = height;
        mCenterX = mWidth / 2;
        mCenterY = mHeight / 2;
        mCircleRadius = mWidth * 5 / 12;
        mBaseLength = mCircleRadius / 3;
        mBaseRippleLength = 4.4f * mBaseLength / 12;
        mCurrentRippleX = mCenterX - mBaseRippleLength * 10;
        mRectF = new RectFloat(mCenterX - mCircleRadius, mCenterY - mCircleRadius, mCenterX + mCircleRadius, mCenterY + mCircleRadius);
        mClipRectF = new RectFloat(mCenterX - 6 * mBaseRippleLength, 0, mCenterX + 6 * mBaseRippleLength, mHeight);
        invalidate();
    }

    public void setLineColor(Color color) {
        mPaint.setColor(color);
    }

    public void setBgLineColor(Color color) {
        mBgPaint.setColor(color);
    }

    public void setTextColor(Color color) {
        mTextPaint.setColor(color);
    }

    public void setLineWidth(int lineWidth) {
        mPaint.setStrokeWidth(lineWidth);
    }

    public void setBgLineWidth(int bgLineWidth) {
        mBgPaint.setStrokeWidth(bgLineWidth);
    }

    public void setTextSize(int size) {
        mTextSize = size;
    }

    @Override
    public void onRefreshed(Component component) {
        mWidth = component.getWidth();
        mHeight = component.getHeight();
        mCenterX = mWidth / 2;
        mCenterY = mHeight / 2;
        mCircleRadius = mWidth * 5 / 12;
        mBaseLength = mCircleRadius / 3;
        mBaseRippleLength = 4.4f * mBaseLength / 12;
        mCurrentRippleX = mCenterX - mBaseRippleLength * 10;
        mRectF = new RectFloat(mCenterX - mCircleRadius, mCenterY - mCircleRadius, mCenterX + mCircleRadius, mCenterY + mCircleRadius);
        mClipRectF = new RectFloat(mCenterX - 6 * mBaseRippleLength, 0, mCenterX + 6 * mBaseRippleLength, mHeight);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (!isInit) {
            isInit = true;
            onRefreshed(component);
        }
        switch (mCurrentState) {
            case STATE_NONE:
            case STATE_PRE: //嗷~ 开始阶段：线弹起小球
                if (mFraction <= 0.4) {
                    canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
                    canvas.drawLine(mCenterX - mBaseLength, mCenterY, mCenterX, mCenterY + mBaseLength, mPaint);
                    canvas.drawLine(mCenterX, mCenterY + mBaseLength, mCenterX + mBaseLength, mCenterY, mPaint);
                    canvas.drawLine(mCenterX, mCenterY + mBaseLength - 1.3f * mBaseLength / 0.4f * mFraction,
                            mCenterX, mCenterY - 1.6f * mBaseLength + 1.3f * mBaseLength / 0.4f * mFraction, mPaint);
                } else if (mFraction <= 0.6) {
                    canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
                    canvas.drawCircle(mCenterX, mCenterY - 0.3f * mBaseLength, 2, mPaint);
                    canvas.drawLine(mCenterX - mBaseLength - mBaseLength * 1.2f / 0.2f * (mFraction - 0.4f), mCenterY, mCenterX, mCenterY + mBaseLength - mBaseLength / 0.2f * (mFraction - 0.4f), mPaint);
                    canvas.drawLine(mCenterX, mCenterY + mBaseLength - mBaseLength / 0.2f * (mFraction - 0.4f), mCenterX + mBaseLength + mBaseLength * 1.2f / 0.2f * (mFraction - 0.4f), mCenterY, mPaint);
                } else if (mFraction <= 1) {
                    canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
                    canvas.drawCircle(mCenterX, mCenterY - 0.3f * mBaseLength - (mCircleRadius - 0.3f * mBaseLength) / 0.4f * (mFraction - 0.6f), 2, mPaint);
                    canvas.drawLine(mCenterX - mBaseLength * 2.2f, mCenterY, mCenterX + mBaseLength * 2.2f, mCenterY, mPaint);
                } else {
                    canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
                    canvas.drawCircle(mCenterX, mCenterY - mCircleRadius - mBaseLength * 3 * (mFraction - 1), 3, mPaint);
                    canvas.drawLine(mCenterX - mBaseLength * 2.2f, mCenterY, mCenterX + mBaseLength * 2.2f, mCenterY, mPaint);
                }
                break;
            case STATE_DOWNLOADING: //嗷~ 下载阶段：波浪线与文字
                if (mFraction <= 0.2) {
                    mTextPaint.setTextSize((int) (mTextSize / 0.2f * mFraction));
                }
                canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
                canvas.drawArc(mRectF, new Arc(-90, 359.99f * mFraction, false), mPaint);
                mPath.reset();
                mCurrentRippleX += DEFAULT_RIPPLE_SPEED;
                if (mCurrentRippleX > mCenterX - mBaseRippleLength * 6)
                    mCurrentRippleX = mCenterX - mBaseRippleLength * 10;

                mPath.moveTo(mCurrentRippleX, mCenterY);
                for (int i = 0; i < 4; i++) {
                    mPath.rQuadTo(mBaseRippleLength, -(1 - mFraction) * mBaseRippleLength, mBaseRippleLength * 2, 0);
                    mPath.rQuadTo(mBaseRippleLength, (1 - mFraction) * mBaseRippleLength, mBaseRippleLength * 2, 0);
                }
                canvas.save();
                canvas.clipRect(mClipRectF);
                canvas.drawPath(mPath, mPaint);
                canvas.restore();
                if (mUnit != DownloadUnit.NONE && mCurrentSize > 0) {
                    canvas.drawText(mTextPaint, String.format("%.2f", mCurrentSize) + getUnitStr(mUnit), mCenterX, mCenterY + 1.4f * mBaseLength);
                }
                break;
            case STATE_END: //嗷~ 结束阶段：线变勾
                canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mPaint);
                if (mFraction <= 0.5) {
                    int textSize = ((int) (mTextSize - mTextSize / 0.2f * mFraction));
                    if (textSize < 0) textSize = 0;
                    mTextPaint.setTextSize(textSize);
                } else {
                    mTextPaint.setTextSize(0);
                }
                if (mUnit != DownloadUnit.NONE && mCurrentSize > 0) {
                    canvas.drawText(mTextPaint, String.format("%.2f", mCurrentSize) + getUnitStr(mUnit), mCenterX, mCenterY + 1.4f * mBaseLength);
                }
                canvas.drawLine(mCenterX - mBaseLength * 2.2f + mBaseLength * 1.2f * mFraction, mCenterY,
                        mCenterX - mBaseLength * 0.5f, mCenterY + mBaseLength * 0.5f * mFraction * 1.3f, mPaint);
                canvas.drawLine(mCenterX - mBaseLength * 0.5f, mCenterY + mBaseLength * 0.5f * mFraction * 1.3f,
                        mCenterX + mBaseLength * 2.2f - mBaseLength * mFraction, mCenterY - mBaseLength * mFraction * 1.3f, mPaint);
                break;
            case STATE_RESET:   //嗷~ 复位阶段：勾变箭头
                canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mBgPaint);
                canvas.drawLine(mCenterX - mBaseLength, mCenterY,
                        mCenterX - mBaseLength * 0.5f + mBaseLength * 0.5f * mFraction, mCenterY + mBaseLength * 0.65f + mBaseLength * 0.35f * mFraction, mPaint);
                canvas.drawLine(mCenterX - mBaseLength * 0.5f + mBaseLength * 0.5f * mFraction, mCenterY + mBaseLength * 0.65f + mBaseLength * 0.35f * mFraction,
                        mCenterX + mBaseLength * 1.2f - mBaseLength * 0.2f * mFraction, mCenterY - 1.3f * mBaseLength + 1.3f * mBaseLength * mFraction, mPaint);
                canvas.drawLine(mCenterX - mBaseLength * 0.5f + mBaseLength * 0.5f * mFraction, mCenterY + mBaseLength * 0.65f + mBaseLength * 0.35f * mFraction,
                        mCenterX - mBaseLength * 0.5f + mBaseLength * 0.5f * mFraction,
                        mCenterY + mBaseLength * 0.65f - mBaseLength * 2.25f * mFraction, mPaint);
                break;
        }
    }

    public void start() {
        if (mCurrentState != STATE_NONE) {
            return;
        }
        mCurrentState = STATE_PRE;
        AnimatorValue preAnim = new AnimatorValue();
        preAnim.setCurveType(Animator.CurveType.OVERSHOOT);
        preAnim.setDuration(1500);
        preAnim.setValueUpdateListener((animatorValue, v) -> {
            mFraction = v;
            invalidate();
        });
        preAnim.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                mFraction = 0f;
                mCurrentState = STATE_DOWNLOADING;
                downloadAnim();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        preAnim.start();
    }

    private void downloadAnim() {
        AnimatorValue downloadAnim = new AnimatorValue();
        downloadAnim.setDuration(mDownloadTime);
        downloadAnim.setCurveType(Animator.CurveType.LINEAR);
        downloadAnim.setValueUpdateListener((animatorValue, v) -> {
            mFraction = v;
            if (mUnit != DownloadUnit.NONE && mTotalSize > 0)
                mCurrentSize = mFraction * mTotalSize;
            invalidate();
        });
        downloadAnim.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                mFraction = 0;
                mCurrentState = STATE_END;
                endAnim();
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        downloadAnim.start();
    }

    private void endAnim() {
        AnimatorValue endAnim = new AnimatorValue();
        endAnim.setDuration(700);
        endAnim.setCurveType(Animator.CurveType.OVERSHOOT);
        endAnim.setValueUpdateListener((animatorValue, v) -> {
            mFraction = v;
            invalidate();
        });
        endAnim.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                mFraction = 0;
                mCurrentState = STATE_RESET;
                if (onDownloadStateListener != null) {
                    onDownloadStateListener.onDownloadFinish();
                }
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        endAnim.start();
    }

    public void reset() {
        if (mCurrentState != STATE_RESET) {
            return;
        }
        AnimatorValue resetAnim = new AnimatorValue();
        resetAnim.setDuration(500);
        resetAnim.setCurveType(Animator.CurveType.OVERSHOOT);
        resetAnim.setValueUpdateListener((animatorValue, v) -> {
            mFraction = v;
            invalidate();
        });
        resetAnim.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                mFraction = 0;
                mCurrentState = STATE_NONE;
                if (onDownloadStateListener != null) {
                    onDownloadStateListener.onResetFinish();
                }
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        resetAnim.start();
    }

    private String getUnitStr(DownloadUnit unit) {
        switch (unit) {
            case GB:
                return " gb";
            case MB:
                return " mb";
            case KB:
                return " kb";
            case B:
                return " b";
        }
        return " b";
    }

    public void setDownloadConfig(int downloadTime, double downloadFileSize, DownloadUnit unit) {
        mDownloadTime = downloadTime;
        mTotalSize = downloadFileSize;
        mUnit = unit;
    }

    public int getCurrentState() {
        return mCurrentState;
    }

    interface OnDownloadStateListener {

        void onDownloadFinish();

        void onResetFinish();
    }

    public void setOnDownloadStateListener(OnDownloadStateListener onDownloadStateListener) {
        this.onDownloadStateListener = onDownloadStateListener;
    }
}
